/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.

This file is part of Quake III Arena source code.

Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.

Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Foobar; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
===========================================================================
*/

/*****************************************************************************
 * name:		l_script.c
 *
 * desc:		lexicographical parser
 *
 * $Archive: /code/botlib/l_script.c $
 *
 *****************************************************************************/

//#define SCREWUP
//#define BOTLIB
//#define MEQCC
//#define BSPC

#ifdef SCREWUP
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <string.h>
#include <stdarg.h>
#include "l_memory.h"
#include "l_script.h"

typedef enum {qfalse, qtrue}	qboolean;

#endif //SCREWUP

#ifdef BOTLIB
//include files for usage in the bot library
#include "../game/q_shared.h"
#include "../game/botlib.h"
#include "be_interface.h"
#include "l_script.h"
#include "l_memory.h"
#include "l_log.h"
#include "l_libvar.h"
#endif //BOTLIB

#ifdef MEQCC
//include files for usage in MrElusive's QuakeC Compiler
#include "qcc.h"
#include "l_script.h"
#include "l_memory.h"
#include "l_log.h"

#define qtrue	true
#define qfalse	false
#endif //MEQCC

#ifdef BSPC
//include files for usage in the BSP Converter
#include "../bspc/qbsp.h"
#include "../bspc/l_log.h"
#include "../bspc/l_mem.h"

#define qtrue	true
#define qfalse	false
#endif //BSPC


#define PUNCTABLE

//longer punctuations first
punctuation_t default_punctuations[] =
    {
        //binary operators
        {">>=",P_RSHIFT_ASSIGN, NULL},
        {"<<=",P_LSHIFT_ASSIGN, NULL},
        //
        {"...",P_PARMS, NULL},
        //define merge operator
        {"##",P_PRECOMPMERGE, NULL},
        //logic operators
        {"&&",P_LOGIC_AND, NULL},
        {"||",P_LOGIC_OR, NULL},
        {">=",P_LOGIC_GEQ, NULL},
        {"<=",P_LOGIC_LEQ, NULL},
        {"==",P_LOGIC_EQ, NULL},
        {"!=",P_LOGIC_UNEQ, NULL},
        //arithmatic operators
        {"*=",P_MUL_ASSIGN, NULL},
        {"/=",P_DIV_ASSIGN, NULL},
        {"%=",P_MOD_ASSIGN, NULL},
        {"+=",P_ADD_ASSIGN, NULL},
        {"-=",P_SUB_ASSIGN, NULL},
        {"++",P_INC, NULL},
        {"--",P_DEC, NULL},
        //binary operators
        {"&=",P_BIN_AND_ASSIGN, NULL},
        {"|=",P_BIN_OR_ASSIGN, NULL},
        {"^=",P_BIN_XOR_ASSIGN, NULL},
        {">>",P_RSHIFT, NULL},
        {"<<",P_LSHIFT, NULL},
        //reference operators
        {"->",P_POINTERREF, NULL},
        //C++
        {"::",P_CPP1, NULL},
        {".*",P_CPP2, NULL},
        //arithmatic operators
        {"*",P_MUL, NULL},
        {"/",P_DIV, NULL},
        {"%",P_MOD, NULL},
        {"+",P_ADD, NULL},
        {"-",P_SUB, NULL},
        {"=",P_ASSIGN, NULL},
        //binary operators
        {"&",P_BIN_AND, NULL},
        {"|",P_BIN_OR, NULL},
        {"^",P_BIN_XOR, NULL},
        {"~",P_BIN_NOT, NULL},
        //logic operators
        {"!",P_LOGIC_NOT, NULL},
        {">",P_LOGIC_GREATER, NULL},
        {"<",P_LOGIC_LESS, NULL},
        //reference operator
        {".",P_REF, NULL},
        //seperators
        {",",P_COMMA, NULL},
        {";",P_SEMICOLON, NULL},
        //label indication
        {":",P_COLON, NULL},
        //if statement
        {"?",P_QUESTIONMARK, NULL},
        //embracements
        {"(",P_PARENTHESESOPEN, NULL},
        {")",P_PARENTHESESCLOSE, NULL},
        {"{",P_BRACEOPEN, NULL},
        {"}",P_BRACECLOSE, NULL},
        {"[",P_SQBRACKETOPEN, NULL},
        {"]",P_SQBRACKETCLOSE, NULL},
        //
        {"\\",P_BACKSLASH, NULL},
        //precompiler operator
        {"#",P_PRECOMP, NULL},
#ifdef DOLLAR
        {"$",P_DOLLAR, NULL},
#endif //DOLLAR
        {NULL, 0}
    };

#ifdef BSPC
char basefolder[MAX_PATH];
#else
char basefolder[MAX_QPATH];
#endif

//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void PS_CreatePunctuationTable(script_t *script, punctuation_t *punctuations)
{
    int i;
    punctuation_t *p, *lastp, *newp;

    //get memory for the table
    if (!script->punctuationtable) script->punctuationtable = (punctuation_t **)
                GetMemory(256 * sizeof(punctuation_t *));
    Com_Memset(script->punctuationtable, 0, 256 * sizeof(punctuation_t *));
    //add the punctuations in the list to the punctuation table
    for (i = 0; punctuations[i].p; i++)
    {
        newp = &punctuations[i];
        lastp = NULL;
        //sort the punctuations in this table entry on length (longer punctuations first)
        for (p = script->punctuationtable[(unsigned int) newp->p[0]]; p; p = p->next)
        {
            if (strlen(p->p) < strlen(newp->p))
            {
                newp->next = p;
                if (lastp) lastp->next = newp;
                else script->punctuationtable[(unsigned int) newp->p[0]] = newp;
                break;
            } //end if
            lastp = p;
        } //end for
        if (!p)
        {
            newp->next = NULL;
            if (lastp) lastp->next = newp;
            else script->punctuationtable[(unsigned int) newp->p[0]] = newp;
        } //end if
    } //end for
} //end of the function PS_CreatePunctuationTable
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
char *PunctuationFromNum(script_t *script, int num)
{
    int i;

    for (i = 0; script->punctuations[i].p; i++)
    {
        if (script->punctuations[i].n == num) return script->punctuations[i].p;
    } //end for
    return "unkown punctuation";
} //end of the function PunctuationFromNum
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void QDECL ScriptError(script_t *script, char *str, ...)
{
    char text[1024];
    va_list ap;

    if (script->flags & SCFL_NOERRORS) return;

    va_start(ap, str);
    vsprintf(text, str, ap);
    va_end(ap);
#ifdef BOTLIB
    botimport.Print(PRT_ERROR, "file %s, line %d: %s\n", script->filename, script->line, text);
#endif //BOTLIB
#ifdef MEQCC
    printf("error: file %s, line %d: %s\n", script->filename, script->line, text);
#endif //MEQCC
#ifdef BSPC
    Log_Print("error: file %s, line %d: %s\n", script->filename, script->line, text);
#endif //BSPC
} //end of the function ScriptError
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void QDECL ScriptWarning(script_t *script, char *str, ...)
{
    char text[1024];
    va_list ap;

    if (script->flags & SCFL_NOWARNINGS) return;

    va_start(ap, str);
    vsprintf(text, str, ap);
    va_end(ap);
#ifdef BOTLIB
    botimport.Print(PRT_WARNING, "file %s, line %d: %s\n", script->filename, script->line, text);
#endif //BOTLIB
#ifdef MEQCC
    printf("warning: file %s, line %d: %s\n", script->filename, script->line, text);
#endif //MEQCC
#ifdef BSPC
    Log_Print("warning: file %s, line %d: %s\n", script->filename, script->line, text);
#endif //BSPC
} //end of the function ScriptWarning
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void SetScriptPunctuations(script_t *script, punctuation_t *p)
{
#ifdef PUNCTABLE
    if (p) PS_CreatePunctuationTable(script, p);
    else  PS_CreatePunctuationTable(script, default_punctuations);
#endif //PUNCTABLE
    if (p) script->punctuations = p;
    else script->punctuations = default_punctuations;
} //end of the function SetScriptPunctuations
//============================================================================
// Reads spaces, tabs, C-like comments etc.
// When a newline character is found the scripts line counter is increased.
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
int PS_ReadWhiteSpace(script_t *script)
{
    while(1)
    {
        //skip white space
        while(*script->script_p <= ' ')
        {
            if (!*script->script_p) return 0;
            if (*script->script_p == '\n') script->line++;
            script->script_p++;
        } //end while
        //skip comments
        if (*script->script_p == '/')
        {
            //comments //
            if (*(script->script_p+1) == '/')
            {
                script->script_p++;
                do
                {
                    script->script_p++;
                    if (!*script->script_p) return 0;
                } //end do
                while(*script->script_p != '\n');
                script->line++;
                script->script_p++;
                if (!*script->script_p) return 0;
                continue;
            } //end if
            //comments /* */
            else if (*(script->script_p+1) == '*')
            {
                script->script_p++;
                do
                {
                    script->script_p++;
                    if (!*script->script_p) return 0;
                    if (*script->script_p == '\n') script->line++;
                } //end do
                while(!(*script->script_p == '*' && *(script->script_p+1) == '/'));
                script->script_p++;
                if (!*script->script_p) return 0;
                script->script_p++;
                if (!*script->script_p) return 0;
                continue;
            } //end if
        } //end if
        break;
    } //end while
    return 1;
} //end of the function PS_ReadWhiteSpace
//============================================================================
// Reads an escape character.
//
// Parameter:				script		: script to read from
//								ch				: place to store the read escape character
// Returns:					-
// Changes Globals:		-
//============================================================================
int PS_ReadEscapeCharacter(script_t *script, char *ch)
{
    int c, val, i;

    //step over the leading '\\'
    script->script_p++;
    //determine the escape character
    switch(*script->script_p)
    {
    case '\\': c = '\\'; break;
    case 'n': c = '\n'; break;
    case 'r': c = '\r'; break;
    case 't': c = '\t'; break;
    case 'v': c = '\v'; break;
    case 'b': c = '\b'; break;
    case 'f': c = '\f'; break;
    case 'a': c = '\a'; break;
    case '\'': c = '\''; break;
    case '\"': c = '\"'; break;
    case '\?': c = '\?'; break;
    case 'x':
        {
            script->script_p++;
            for (i = 0, val = 0; ; i++, script->script_p++)
            {
                c = *script->script_p;
                if (c >= '0' && c <= '9') c = c - '0';
                else if (c >= 'A' && c <= 'Z') c = c - 'A' + 10;
                else if (c >= 'a' && c <= 'z') c = c - 'a' + 10;
                else break;
                val = (val << 4) + c;
            } //end for
            script->script_p--;
            if (val > 0xFF)
            {
                ScriptWarning(script, "too large value in escape character");
                val = 0xFF;
            } //end if
            c = val;
            break;
        } //end case
    default: //NOTE: decimal ASCII code, NOT octal
        {
            if (*script->script_p < '0' || *script->script_p > '9') ScriptError(script, "unknown escape char");
            for (i = 0, val = 0; ; i++, script->script_p++)
            {
                c = *script->script_p;
                if (c >= '0' && c <= '9') c = c - '0';
                else break;
                val = val * 10 + c;
            } //end for
            script->script_p--;
            if (val > 0xFF)
            {
                ScriptWarning(script, "too large value in escape character");
                val = 0xFF;
            } //end if
            c = val;
            break;
        } //end default
    } //end switch
    //step over the escape character or the last digit of the number
    script->script_p++;
    //store the escape character
    *ch = c;
    //succesfully read escape character
    return 1;
} //end of the function PS_ReadEscapeCharacter
//============================================================================
// Reads C-like string. Escape characters are interpretted.
// Quotes are included with the string.
// Reads two strings with a white space between them as one string.
//
// Parameter:				script		: script to read from
//								token			: buffer to store the string
// Returns:					qtrue when a string was read succesfully
// Changes Globals:		-
//============================================================================
int PS_ReadString(script_t *script, token_t *token, int quote)
{
    int len, tmpline;
    char *tmpscript_p;

    if (quote == '\"') token->type = TT_STRING;
    else token->type = TT_LITERAL;

    len = 0;
    //leading quote
    token->string[len++] = *script->script_p++;
    //
    while(1)
    {
        //minus 2 because trailing double quote and zero have to be appended
        if (len >= MAX_TOKEN - 2)
        {
            ScriptError(script, "string longer than MAX_TOKEN = %d", MAX_TOKEN);
            return 0;
        } //end if
        //if there is an escape character and
        //if escape characters inside a string are allowed
        if (*script->script_p == '\\' && !(script->flags & SCFL_NOSTRINGESCAPECHARS))
        {
            if (!PS_ReadEscapeCharacter(script, &token->string[len]))
            {
                token->string[len] = 0;
                return 0;
            } //end if
            len++;
        } //end if
        //if a trailing quote
        else if (*script->script_p == quote)
        {
            //step over the double quote
            script->script_p++;
            //if white spaces in a string are not allowed
            if (script->flags & SCFL_NOSTRINGWHITESPACES) break;
            //
            tmpscript_p = script->script_p;
            tmpline = script->line;
            //read unusefull stuff between possible two following strings
            if (!PS_ReadWhiteSpace(script))
            {
                script->script_p = tmpscript_p;
                script->line = tmpline;
                break;
            } //end if
            //if there's no leading double qoute
            if (*script->script_p != quote)
            {
                script->script_p = tmpscript_p;
                script->line = tmpline;
                break;
            } //end if
            //step over the new leading double quote
            script->script_p++;
        } //end if
        else
        {
            if (*script->script_p == '\0')
            {
                token->string[len] = 0;
                ScriptError(script, "missing trailing quote");
                return 0;
            } //end if
            if (*script->script_p == '\n')
            {
                token->string[len] = 0;
                ScriptError(script, "newline inside string %s", token->string);
                return 0;
            } //end if
            token->string[len++] = *script->script_p++;
        } //end else
    } //end while
    //trailing quote
    token->string[len++] = quote;
    //end string with a zero
    token->string[len] = '\0';
    //the sub type is the length of the string
    token->subtype = len;
    return 1;
} //end of the function PS_ReadString
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
int PS_ReadName(script_t *script, token_t *token)
{
    int len = 0;
    char c;

    token->type = TT_NAME;
    do
    {
        token->string[len++] = *script->script_p++;
        if (len >= MAX_TOKEN)
        {
            ScriptError(script, "name longer than MAX_TOKEN = %d", MAX_TOKEN);
            return 0;
        } //end if
        c = *script->script_p;
    } while ((c >= 'a' && c <= 'z') ||
             (c >= 'A' && c <= 'Z') ||
             (c >= '0' && c <= '9') ||
             c == '_');
    token->string[len] = '\0';
    //the sub type is the length of the name
    token->subtype = len;
    return 1;
} //end of the function PS_ReadName
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
void NumberValue(char *string, int subtype, unsigned long int *intvalue,
                 long double *floatvalue)
{
    unsigned long int dotfound = 0;

    *intvalue = 0;
    *floatvalue = 0;
    //floating point number
    if (subtype & TT_FLOAT)
    {
        while(*string)
        {
            if (*string == '.')
            {
                if (dotfound) return;
                dotfound = 10;
                string++;
            } //end if
            if (dotfound)
            {
                *floatvalue = *floatvalue + (long double) (*string - '0') /
                              (long double) dotfound;
                dotfound *= 10;
            } //end if
            else
            {
                *floatvalue = *floatvalue * 10.0 + (long double) (*string - '0');
            } //end else
            string++;
        } //end while
        *intvalue = (unsigned long) *floatvalue;
    } //end if
    else if (subtype & TT_DECIMAL)
    {
        while(*string) *intvalue = *intvalue * 10 + (*string++ - '0');
        *floatvalue = *intvalue;
    } //end else if
    else if (subtype & TT_HEX)
    {
        //step over the leading 0x or 0X
        string += 2;
        while(*string)
        {
            *intvalue <<= 4;
            if (*string >= 'a' && *string <= 'f') *intvalue += *string - 'a' + 10;
            else if (*string >= 'A' && *string <= 'F') *intvalue += *string - 'A' + 10;
            else *intvalue += *string - '0';
            string++;
        } //end while
        *floatvalue = *intvalue;
    } //end else if
    else if (subtype & TT_OCTAL)
    {
        //step over the first zero
        string += 1;
        while(*string) *intvalue = (*intvalue << 3) + (*string++ - '0');
        *floatvalue = *intvalue;
    } //end else if
    else if (subtype & TT_BINARY)
    {
        //step over the leading 0b or 0B
        string += 2;
        while(*string) *intvalue = (*intvalue << 1) + (*string++ - '0');
        *floatvalue = *intvalue;
    } //end else if
} //end of the function NumberValue
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
int PS_ReadNumber(script_t *script, token_t *token)
{
    int len = 0, i;
    int octal, dot;
    char c;
    //	unsigned long int intvalue = 0;
    //	long double floatvalue = 0;

    token->type = TT_NUMBER;
    //check for a hexadecimal number
    if (*script->script_p == '0' &&
            (*(script->script_p + 1) == 'x' ||
             *(script->script_p + 1) == 'X'))
    {
        token->string[len++] = *script->script_p++;
        token->string[len++] = *script->script_p++;
        c = *script->script_p;
        //hexadecimal
        while((c >= '0' && c <= '9') ||
                (c >= 'a' && c <= 'f') ||
                (c >= 'A' && c <= 'A'))
        {
            token->string[len++] = *script->script_p++;
            if (len >= MAX_TOKEN)
            {
                ScriptError(script, "hexadecimal number longer than MAX_TOKEN = %d", MAX_TOKEN);
                return 0;
            } //end if
            c = *script->script_p;
        } //end while
        token->subtype |= TT_HEX;
    } //end if
#ifdef BINARYNUMBERS
    //check for a binary number
    else if (*script->script_p == '0' &&
             (*(script->script_p + 1) == 'b' ||
              *(script->script_p + 1) == 'B'))
    {
        token->string[len++] = *script->script_p++;
        token->string[len++] = *script->script_p++;
        c = *script->script_p;
        //binary
        while(c == '0' || c == '1')
        {
            token->string[len++] = *script->script_p++;
            if (len >= MAX_TOKEN)
            {
                ScriptError(script, "binary number longer than MAX_TOKEN = %d", MAX_TOKEN);
                return 0;
            } //end if
            c = *script->script_p;
        } //end while
        token->subtype |= TT_BINARY;
    } //end if
#endif //BINARYNUMBERS
    else //decimal or octal integer or floating point number
    {
        octal = qfalse;
        dot = qfalse;
        if (*script->script_p == '0') octal = qtrue;
        while(1)
        {
            c = *script->script_p;
            if (c == '.') dot = qtrue;
            else if (c == '8' || c == '9') octal = qfalse;
            else if (c < '0' || c > '9') break;
            token->string[len++] = *script->script_p++;
            if (len >= MAX_TOKEN - 1)
            {
                ScriptError(script, "number longer than MAX_TOKEN = %d", MAX_TOKEN);
                return 0;
            } //end if
        } //end while
        if (octal) token->subtype |= TT_OCTAL;
        else token->subtype |= TT_DECIMAL;
        if (dot) token->subtype |= TT_FLOAT;
    } //end else
    for (i = 0; i < 2; i++)
    {
        c = *script->script_p;
        //check for a LONG number
        if ( (c == 'l' || c == 'L') // bk001204 - brackets
                && !(token->subtype & TT_LONG))
        {
            script->script_p++;
            token->subtype |= TT_LONG;
        } //end if
        //check for an UNSIGNED number
        else if ( (c == 'u' || c == 'U') // bk001204 - brackets
                  && !(token->subtype & (TT_UNSIGNED | TT_FLOAT)))
        {
            script->script_p++;
            token->subtype |= TT_UNSIGNED;
        } //end if
    } //end for
    token->string[len] = '\0';
#ifdef NUMBERVALUE
    NumberValue(token->string, token->subtype, &token->intvalue, &token->floatvalue);
#endif //NUMBERVALUE
    if (!(token->subtype & TT_FLOAT)) token->subtype |= TT_INTEGER;
    return 1;
} //end of the function PS_ReadNumber
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
int PS_ReadLiteral(script_t *script, token_t *token)
{
    token->type = TT_LITERAL;
    //first quote
    token->string[0] = *script->script_p++;
    //check for end of file
    if (!*script->script_p)
    {
        ScriptError(script, "end of file before trailing \'");
        return 0;
    } //end if
    //if it is an escape character
    if (*script->script_p == '\\')
    {
        if (!PS_ReadEscapeCharacter(script, &token->string[1])) return 0;
    } //end if
    else
    {
        token->string[1] = *script->script_p++;
    } //end else
    //check for trailing quote
    if (*script->script_p != '\'')
    {
        ScriptWarning(script, "too many characters in literal, ignored");
        while(*script->script_p &&
                *script->script_p != '\'' &&
                *script->script_p != '\n')
        {
            script->script_p++;
        } //end while
        if (*script->script_p == '\'') script->script_p++;
    } //end if
    //store the trailing quote
    token->string[2] = *script->script_p++;
    //store trailing zero to end the string
    token->string[3] = '\0';
    //the sub type is the integer literal value
    token->subtype = token->string[1];
    //
    return 1;
} //end of the function PS_ReadLiteral
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
int PS_ReadPunctuation(script_t *script, token_t *token)
{
    int len;
    char *p;
    punctuation_t *punc;

#ifdef PUNCTABLE
    for (punc = script->punctuationtable[(unsigned int)*script->script_p]; punc; punc = punc->next)
    {
#else
    int i;

    for (i = 0; script->punctuations[i].p; i++)
    {
        punc = &script->punctuations[i];
#endif //PUNCTABLE
        p = punc->p;
        len = strlen(p);
        //if the script contains at least as much characters as the punctuation
        if (script->script_p + len <= script->end_p)
        {
            //if the script contains the punctuation
            if (!strncmp(script->script_p, p, len))
            {
                strncpy(token->string, p, MAX_TOKEN);
                script->script_p += len;
                token->type = TT_PUNCTUATION;
                //sub type is the number of the punctuation
                token->subtype = punc->n;
                return 1;
            } //end if
        } //end if
    } //end for
    return 0;
} //end of the function PS_ReadPunctuation
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
int PS_ReadPrimitive(script_t *script, token_t *token)
{
    int len;

    len = 0;
    while(*script->script_p > ' ' && *script->script_p != ';')
    {
        if (len >= MAX_TOKEN)
        {
            ScriptError(script, "primitive token longer than MAX_TOKEN = %d", MAX_TOKEN);
            return 0;
        } //end if
        token->string[len++] = *script->script_p++;
    } //end while
    token->string[len] = 0;
    //copy the token into the script structure
    Com_Memcpy(&script->token, token, sizeof(token_t));
    //primitive reading successfull
    return 1;
} //end of the function PS_ReadPrimitive
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
int PS_ReadToken(script_t *script, token_t *token)
{
    //if there is a token available (from UnreadToken)
    if (script->tokenavailable)
    {
        script->tokenavailable = 0;
        Com_Memcpy(token, &script->token, sizeof(token_t));
        return 1;
    } //end if
    //save script pointer
    script->lastscript_p = script->script_p;
    //save line counter
    script->lastline = script->line;
    //clear the token stuff
    Com_Memset(token, 0, sizeof(token_t));
    //start of the white space
    script->whitespace_p = script->script_p;
    token->whitespace_p = script->script_p;
    //read unusefull stuff
    if (!PS_ReadWhiteSpace(script)) return 0;
    //end of the white space
    script->endwhitespace_p = script->script_p;
    token->endwhitespace_p = script->script_p;
    //line the token is on
    token->line = script->line;
    //number of lines crossed before token
    token->linescrossed = script->line - script->lastline;
    //if there is a leading double quote
    if (*script->script_p == '\"')
    {
        if (!PS_ReadString(script, token, '\"')) return 0;
    } //end if
    //if an literal
    else if (*script->script_p == '\'')
    {
        //if (!PS_ReadLiteral(script, token)) return 0;
        if (!PS_ReadString(script, token, '\'')) return 0;
    } //end if
    //if there is a number
    else if ((*script->script_p >= '0' && *script->script_p <= '9') ||
             (*script->script_p == '.' &&
              (*(script->script_p + 1) >= '0' && *(script->script_p + 1) <= '9')))
    {
        if (!PS_ReadNumber(script, token)) return 0;
    } //end if
    //if this is a primitive script
    else if (script->flags & SCFL_PRIMITIVE)
    {
        return PS_ReadPrimitive(script, token);
    } //end else if
    //if there is a name
    else if ((*script->script_p >= 'a' && *script->script_p <= 'z') ||
             (*script->script_p >= 'A' && *script->script_p <= 'Z') ||
             *script->script_p == '_')
    {
        if (!PS_ReadName(script, token)) return 0;
    } //end if
    //check for punctuations
    else if (!PS_ReadPunctuation(script, token))
    {
        ScriptError(script, "can't read token");
        return 0;
    } //end if
    //copy the token into the script structure
    Com_Memcpy(&script->token, token, sizeof(token_t));
    //succesfully read a token
    return 1;
} //end of the function PS_ReadToken
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
int PS_ExpectTokenString(script_t *script, char *string)
{
    token_t token;

    if (!PS_ReadToken(script, &token))
    {
        ScriptError(script, "couldn't find expected %s", string);
        return 0;
    } //end if

    if (strcmp(token.string, string))
    {
        ScriptError(script, "expected %s, found %s", string, token.string);
        return 0;
    } //end if
    return 1;
} //end of the function PS_ExpectToken
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
int PS_ExpectTokenType(script_t *script, int type, int subtype, token_t *token)
{
    char str[MAX_TOKEN];

    if (!PS_ReadToken(script, token))
    {
        ScriptError(script, "couldn't read expected token");
        return 0;
    } //end if

    if (token->type != type)
    {
        if (type == TT_STRING) strcpy(str, "string");
        if (type == TT_LITERAL) strcpy(str, "literal");
        if (type == TT_NUMBER) strcpy(str, "number");
        if (type == TT_NAME) strcpy(str, "name");
        if (type == TT_PUNCTUATION) strcpy(str, "punctuation");
        ScriptError(script, "expected a %s, found %s", str, token->string);
        return 0;
    } //end if
    if (token->type == TT_NUMBER)
    {
        if ((token->subtype & subtype) != subtype)
        {
            if (subtype & TT_DECIMAL) strcpy(str, "decimal");
            if (subtype & TT_HEX) strcpy(str, "hex");
            if (subtype & TT_OCTAL) strcpy(str, "octal");
            if (subtype & TT_BINARY) strcpy(str, "binary");
            if (subtype & TT_LONG) strcat(str, " long");
            if (subtype & TT_UNSIGNED) strcat(str, " unsigned");
            if (subtype & TT_FLOAT) strcat(str, " float");
            if (subtype & TT_INTEGER) strcat(str, " integer");
            ScriptError(script, "expected %s, found %s", str, token->string);
            return 0;
        } //end if
    } //end if
    else if (token->type == TT_PUNCTUATION)
    {
        if (subtype < 0)
        {
            ScriptError(script, "BUG: wrong punctuation subtype");
            return 0;
        } //end if
        if (token->subtype != subtype)
        {
            ScriptError(script, "expected %s, found %s",
                        script->punctuations[subtype], token->string);
            return 0;
        } //end if
    } //end else if
    return 1;
} //end of the function PS_ExpectTokenType
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
int PS_ExpectAnyToken(script_t *script, token_t *token)
{
    if (!PS_ReadToken(script, token))
    {
        ScriptError(script, "couldn't read expected token");
        return 0;
    } //end if
    else
    {
        return 1;
    } //end else
} //end of the function PS_ExpectAnyToken
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
int PS_CheckTokenString(script_t *script, char *string)
{
    token_t tok;

    if (!PS_ReadToken(script, &tok)) return 0;
    //if the token is available
    if (!strcmp(tok.string, string)) return 1;
    //token not available
    script->script_p = script->lastscript_p;
    return 0;
} //end of the function PS_CheckTokenString
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
int PS_CheckTokenType(script_t *script, int type, int subtype, token_t *token)
{
    token_t tok;

    if (!PS_ReadToken(script, &tok)) return 0;
    //if the type matches
    if (tok.type == type &&
            (tok.subtype & subtype) == subtype)
    {
        Com_Memcpy(token, &tok, sizeof(token_t));
        return 1;
    } //end if
    //token is not available
    script->script_p = script->lastscript_p;
    return 0;
} //end of the function PS_CheckTokenType
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
int PS_SkipUntilString(script_t *script, char *string)
{
    token_t token;

    while(PS_ReadToken(script, &token))
    {
        if (!strcmp(token.string, string)) return 1;
    } //end while
    return 0;
} //end of the function PS_SkipUntilString
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
void PS_UnreadLastToken(script_t *script)
{
    script->tokenavailable = 1;
} //end of the function UnreadLastToken
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
void PS_UnreadToken(script_t *script, token_t *token)
{
    Com_Memcpy(&script->token, token, sizeof(token_t));
    script->tokenavailable = 1;
} //end of the function UnreadToken
//============================================================================
// returns the next character of the read white space, returns NULL if none
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
char PS_NextWhiteSpaceChar(script_t *script)
{
    if (script->whitespace_p != script->endwhitespace_p)
    {
        return *script->whitespace_p++;
    } //end if
    else
    {
        return 0;
    } //end else
} //end of the function PS_NextWhiteSpaceChar
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
void StripDoubleQuotes(char *string)
{
    if (*string == '\"')
    {
        strcpy(string, string+1);
    } //end if
    if (string[strlen(string)-1] == '\"')
    {
        string[strlen(string)-1] = '\0';
    } //end if
} //end of the function StripDoubleQuotes
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
void StripSingleQuotes(char *string)
{
    if (*string == '\'')
    {
        strcpy(string, string+1);
    } //end if
    if (string[strlen(string)-1] == '\'')
    {
        string[strlen(string)-1] = '\0';
    } //end if
} //end of the function StripSingleQuotes
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
long double ReadSignedFloat(script_t *script)
{
    token_t token;
    long double sign = 1;

    PS_ExpectAnyToken(script, &token);
    if (!strcmp(token.string, "-"))
    {
        sign = -1;
        PS_ExpectTokenType(script, TT_NUMBER, 0, &token);
    } //end if
    else if (token.type != TT_NUMBER)
    {
        ScriptError(script, "expected float value, found %s\n", token.string);
    } //end else if
    return sign * token.floatvalue;
} //end of the function ReadSignedFloat
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
signed long int ReadSignedInt(script_t *script)
{
    token_t token;
    signed long int sign = 1;

    PS_ExpectAnyToken(script, &token);
    if (!strcmp(token.string, "-"))
    {
        sign = -1;
        PS_ExpectTokenType(script, TT_NUMBER, TT_INTEGER, &token);
    } //end if
    else if (token.type != TT_NUMBER || token.subtype == TT_FLOAT)
    {
        ScriptError(script, "expected integer value, found %s\n", token.string);
    } //end else if
    return sign * token.intvalue;
} //end of the function ReadSignedInt
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
void SetScriptFlags(script_t *script, int flags)
{
    script->flags = flags;
} //end of the function SetScriptFlags
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
int GetScriptFlags(script_t *script)
{
    return script->flags;
} //end of the function GetScriptFlags
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
void ResetScript(script_t *script)
{
    //pointer in script buffer
    script->script_p = script->buffer;
    //pointer in script buffer before reading token
    script->lastscript_p = script->buffer;
    //begin of white space
    script->whitespace_p = NULL;
    //end of white space
    script->endwhitespace_p = NULL;
    //set if there's a token available in script->token
    script->tokenavailable = 0;
    //
    script->line = 1;
    script->lastline = 1;
    //clear the saved token
    Com_Memset(&script->token, 0, sizeof(token_t));
} //end of the function ResetScript
//============================================================================
// returns true if at the end of the script
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
int EndOfScript(script_t *script)
{
    return script->script_p >= script->end_p;
} //end of the function EndOfScript
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
int NumLinesCrossed(script_t *script)
{
    return script->line - script->lastline;
} //end of the function NumLinesCrossed
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
int ScriptSkipTo(script_t *script, char *value)
{
    int len;
    char firstchar;

    firstchar = *value;
    len = strlen(value);
    do
    {
        if (!PS_ReadWhiteSpace(script)) return 0;
        if (*script->script_p == firstchar)
        {
            if (!strncmp(script->script_p, value, len))
            {
                return 1;
            } //end if
        } //end if
        script->script_p++;
    } while(1);
} //end of the function ScriptSkipTo
#ifndef BOTLIB
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
int FileLength(FILE *fp)
{
    int pos;
    int end;

    pos = ftell(fp);
    fseek(fp, 0, SEEK_END);
    end = ftell(fp);
    fseek(fp, pos, SEEK_SET);

    return end;
} //end of the function FileLength
#endif
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
script_t *LoadScriptFile(const char *filename)
{
#ifdef BOTLIB
    fileHandle_t fp;
    char pathname[MAX_QPATH];
#else
    FILE *fp;
#endif
    int length;
    void *buffer;
    script_t *script;

#ifdef BOTLIB
    if (strlen(basefolder))
        Com_sprintf(pathname, sizeof(pathname), "%s/%s", basefolder, filename);
    else
        Com_sprintf(pathname, sizeof(pathname), "%s", filename);
    length = botimport.FS_FOpenFile( pathname, &fp, FS_READ );
    if (!fp) return NULL;
#else
    fp = fopen(filename, "rb");
    if (!fp) return NULL;

    length = FileLength(fp);
#endif

    buffer = GetClearedMemory(sizeof(script_t) + length + 1);
    script = (script_t *) buffer;
    Com_Memset(script, 0, sizeof(script_t));
    strcpy(script->filename, filename);
    script->buffer = (char *) buffer + sizeof(script_t);
    script->buffer[length] = 0;
    script->length = length;
    //pointer in script buffer
    script->script_p = script->buffer;
    //pointer in script buffer before reading token
    script->lastscript_p = script->buffer;
    //pointer to end of script buffer
    script->end_p = &script->buffer[length];
    //set if there's a token available in script->token
    script->tokenavailable = 0;
    //
    script->line = 1;
    script->lastline = 1;
    //
    SetScriptPunctuations(script, NULL);
    //
#ifdef BOTLIB
    botimport.FS_Read(script->buffer, length, fp);
    botimport.FS_FCloseFile(fp);
#else
    if (fread(script->buffer, length, 1, fp) != 1)
    {
        FreeMemory(buffer);
        script = NULL;
    } //end if
    fclose(fp);
#endif
    //
    script->length = COM_Compress(script->buffer);

    return script;
} //end of the function LoadScriptFile
//============================================================================
//
// Parameter:			-
// Returns:				-
// Changes Globals:		-
//============================================================================
script_t *LoadScriptMemory(char *ptr, int length, char *name)
{
    void *buffer;
    script_t *script;

    buffer = GetClearedMemory(sizeof(script_t) + length + 1);
    script = (script_t *) buffer;
    Com_Memset(script, 0, sizeof(script_t));
    strcpy(script->filename, name);
    script->buffer = (char *) buffer + sizeof(script_t);
    script->buffer[length] = 0;
    script->length = length;
    //pointer in script buffer
    script->script_p = script->buffer;
    //pointer in script buffer before reading token
    script->lastscript_p = script->buffer;
    //pointer to end of script buffer
    script->end_p = &script->buffer[length];
    //set if there's a token available in script->token
    script->tokenavailable = 0;
    //
    script->line = 1;
    script->lastline = 1;
    //
    SetScriptPunctuations(script, NULL);
    //
    Com_Memcpy(script->buffer, ptr, length);
    //
    return script;
} //end of the function LoadScriptMemory
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
void FreeScript(script_t *script)
{
#ifdef PUNCTABLE
    if (script->punctuationtable) FreeMemory(script->punctuationtable);
#endif //PUNCTABLE
    FreeMemory(script);
} //end of the function FreeScript
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
void PS_SetBaseFolder(char *path)
{
#ifdef BSPC
    sprintf(basefolder, path);
#else
    Com_sprintf(basefolder, sizeof(basefolder), path);
#endif
} //end of the function PS_SetBaseFolder
